package de.popforge.audio
{
	import __AS3__.vec.Vector;
	
	import de.popforge.audio.driver.AudioDriver;
	import de.popforge.audio.driver.IAudioProcessor;
	
	import flash.utils.ByteArray;	

	/**
	 * Georg Feil - Synth Sample 1985
	 * 
	 * This is not considered as a tutorial rather optimized to smaller file size.
	 * 
	 * FP10 Sound Demo
	 * 
	 * @author Andre Michelle
	 */
	public class SynthSample
		implements IAudioProcessor
	{
		[ Embed( source = 'SynthSample.bassline', mimeType='application/octet-stream' ) ] private const BASSLINE: Class;
		[ Embed( source = 'SynthSample.melody', mimeType='application/octet-stream' ) ] private const MELODY: Class;

		//-- SOUND REGISTER
		//-- PitchPhase, PitchPhaseIncr, PulsePhase, Volume, Pan, LowPass
		private const sr: Vector.<Number> = new Vector.<Number>( 18, true );

		//-- DATA
		private const bd: ByteArray = ByteArray( new BASSLINE() );
		private const md: ByteArray = ByteArray( new MELODY() );
		
		//-- QUARTER INDEX AND SAMPLE POSITION
		private var qi: int;
		private var qs: int;
		
		//-- MELODY SHORT AND SAMPLE POSITION
		private var msh: int;
		private var ms: int;
		
		//-- MASTER MIXER
		private var run: Boolean;
		private var vol: Number;
		
		private var active: Boolean;

		public function SynthSample()
		{
			bd.uncompress();
			md.uncompress();
			md.position = 0;

			qi = 0;
			qs = 0;
			
			//-- FIRST MELO NOTE
			msh = md.readShort();
			ms = int( ( ( ( msh >> 8 & 0xFF ) * .125 ) * 10584000.0 ) / 170.0 );
			
			//-- PANNING
			sr[4] = .25;
			sr[10] = -.25;
			
			run = true;
			vol = .2;
			
			active = true;
		}

		public function process( buffer: Vector.<Vector.<Number>> ): void
		{
			if( !active )
				return;
			
			var le: Vector.<Number> = buffer[0];
			var ri: Vector.<Number> = buffer[1];
			
			var amp: Number;
			var ml: Number;
			var mr: Number;
			var pl: Number;
			var vi: int;
			var vn: int;
			
			var i: int = 0;
			var n: int = AudioDriver.SIZE;
			
			for( ; i < n ; ++i )
			{
				if( --qs < 0 ) //-- NEXT QUARTER NOTE
				{
					//-- SET REGISTER
					vn = (qi&1) * 6;
					
					// PitchPhase
					sr[int(vn+0)] = 0.0;
					
					// PitchPhaseIncr
					sr[int(vn+1)] = 440.0 * Math.pow( 2, ( bd[ qi ] - 69.0 ) / 12.0 ) / 44100.0;
					
					// Volume
					sr[int(vn+3)] = .25;
					
					// QUATER NOTE IN SAMPLES (170BPM)
					qs = int( ( .25 * 10584000.0 ) / 170.0 );
					++qi;
				}
				
				if( md.bytesAvailable >= 2 ) // MELODY DATA AVAIABLE
				{
					if( --ms < 0 )  // NEXT MELODY NOTE
					{
						// PitchPhase
						sr[12] = 0.0;

						// PitchPhaseIncr
						sr[13] = 440.0 * Math.pow( 2, ( ( msh & 0xFF ) - 69.0 ) / 12.0 ) / 44100.0;
						
						// Volume
						sr[15] = .175;
						
						//-- COMPUTE SAMPLE OFFSET TO NEXT NOTE
						msh = md.readShort();
						ms = int( ( ( ( msh >> 8 & 0xFF ) * .125 ) * 10584000.0 ) / 170.0 );
					}
				}
				else
					run = false;
				
				//-- MASTER VOLUME
				if( run )
					vol += ( 1.0 - vol ) * .000003;
				else
					vol -= vol * .000003;
				
				//-- MIX UP THE 3 CHANNELS
				ml = 0.0;
				mr = 0.0;
				
				for( vi = 0 ; vi < 3 ; ++vi )
				{
					vn = vi * 6;
					
					//-- PULSE
					if( ( pl = sr[int(vn+2)] * 2 ) > 1 ) pl = 2 - pl;
					
					//-- WAVEFORM (SQUARE WITH PULSE-MODULATION)
					amp = sr[vn] < ( .2 + pl * .3 ) ? vol : -vol;
					
					//-- VOLUME DECAY ENVELOPE
					amp *= sr[int(vn+3)] *= .99996;
					
					//-- LOWPASS FILTER TO AVOID ANTIALIASING
					sr[int(vn+5)] += ( amp - sr[int(vn+5)] ) * .3;
					
					//-- VOICE MIX
					ml += ( 1 - sr[int(vn+4)] ) * sr[int(vn+5)];
					mr += ( sr[int(vn+4)] + 1 ) * sr[int(vn+5)];

					//-- PULSE INCR
					if( ( sr[int(vn+2)] += .000033 ) >= 1 )
						--sr[int(vn+2)];

					//-- PITCH INCR
					if( ( sr[vn] += sr[int(vn+1)] ) >= 1 )
						--sr[vn];
				}

				//-- WRITE OUTPUT STREAM
				le[i] = ml;
				ri[i] = mr;
			}
			
			if( vol < .001 )
				active  = false;
		}
	}
}